import wx.stc as stc
import wx


class Book_stc(stc.StyledTextCtrl):
    def __init__(self, parent, file_name, stc_type, **k):
        stc.StyledTextCtrl.__init__(self, parent, -1)
        
        self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
        self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
        
        self.SetLexer(stc_type)

        self.SetProperty("fold", "1")
        self.SetMargins(0,0)

        self.SetViewWhiteSpace(False)

        self.SetWrapMode(stc.STC_WRAP_CHAR)

        self.SetMarginType(2, stc.STC_MARGIN_SYMBOL)
        self.SetMarginType(1, stc.STC_MARGIN_NUMBER)
        self.SetMarginMask(2, stc.STC_MASK_FOLDERS)
        self.SetMarginSensitive(2, True)
        self.SetMarginWidth(1, 20)
        self.SetMarginWidth(2, 12)

        self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN,    stc.STC_MARK_BOXMINUS,          "white", "#808080")
        self.MarkerDefine(stc.STC_MARKNUM_FOLDER,        stc.STC_MARK_BOXPLUS,           "white", "#808080")
        self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB,     stc.STC_MARK_VLINE,             "white", "#808080")
        self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL,    stc.STC_MARK_LCORNER,           "white", "#808080")
        self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND,     stc.STC_MARK_BOXPLUSCONNECTED,  "white", "#808080")
        self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_BOXMINUSCONNECTED, "white", "#808080")
        self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNER,           "white", "#808080")

        self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdateUI)
        self.Bind(stc.EVT_STC_MARGINCLICK, self.OnMarginClick)

        self.StyleClearAll()  # Reset all to be like the default

        faces = { 
        	'times': 'Times New Roman',
    		'mono' : 'Courier New',
    		'helv' : 'Arial',
    		'other': 'Comic Sans MS',
    		'size' : 10,
    		'size2': 8,
			}
        # Global default styles for all languages
        self.StyleSetSpec(stc.STC_STYLE_DEFAULT,     "face:%(helv)s,size:%(size)d" % faces)
        self.StyleSetSpec(stc.STC_STYLE_LINENUMBER,  "back:#C0C0C0,face:%(helv)s,size:%(size2)d" % faces)
        self.StyleSetSpec(stc.STC_STYLE_CONTROLCHAR, "face:%(other)s" % faces)
        self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,  "fore:#FFFFFF,back:#0000FF,bold")
        self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,    "fore:#000000,back:#FF0000,bold")
        if stc_type == stc.STC_LEX_PYTHON:
            py_key(self)
        elif stc_type == stc.STC_LEX_LUA:
            lua_key(self)
        elif stc_type == stc.STC_LEX_RUST:
            rust_key(self)
        elif stc_type == stc.STC_LEX_CPP:
            go_key(self)
        elif stc_type == stc.STC_LEX_CONF:
            pass
        # my styles
        # Default
        self.StyleSetSpec(stc.STC_P_DEFAULT,      "fore:#000000,face:%(helv)s,size:%(size)d" % faces)
        # Comments
        self.StyleSetSpec(stc.STC_P_COMMENTLINE,  "fore:#007F00,face:%(other)s,size:%(size)d" % faces)
        # Number
        self.StyleSetSpec(stc.STC_P_NUMBER,       "fore:#007F7F,size:%(size)d" % faces)
        # String
        self.StyleSetSpec(stc.STC_P_STRING,       "fore:#7F007F,face:%(helv)s,size:%(size)d" % faces)
        # Single quoted string
        self.StyleSetSpec(stc.STC_P_CHARACTER,    "fore:#7F007F,face:%(helv)s,size:%(size)d" % faces)
        # Keyword
        self.StyleSetSpec(stc.STC_P_WORD,         "fore:#00007F,bold,size:%(size)d" % faces)
        # Triple quotes
        self.StyleSetSpec(stc.STC_P_TRIPLE,       "fore:#7F0000,size:%(size)d" % faces)
        # Triple double quotes
        self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, "fore:#7F0000,size:%(size)d" % faces)
        # Class name definition
        self.StyleSetSpec(stc.STC_P_CLASSNAME,    "fore:#0000FF,bold,size:%(size)d" % faces)
        # Function or method name definition
        self.StyleSetSpec(stc.STC_P_DEFNAME,      "fore:#007F7F,bold,size:%(size)d" % faces)
        # Operators
        self.StyleSetSpec(stc.STC_P_OPERATOR,     "bold,size:%(size)d" % faces)
        # Identifiers
        self.StyleSetSpec(stc.STC_P_IDENTIFIER,   "fore:#000000,face:%(helv)s,size:%(size)d" % faces)
        # Comment-blocks
        self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, "fore:#7F7F7F,size:%(size)d" % faces)
        # End of line where string is not closed
        self.StyleSetSpec(stc.STC_P_STRINGEOL,    "fore:#000000,face:%(mono)s,back:#E0C0E0,eol,size:%(size)d" % faces)

        self.SetCaretForeground("GREEN")
        self.SetCaretLineBackground("BROWN")
        self.SetCaretWidth(8)
        self.SetCaretStyle(stc.STC_CARETSTYLE_LINE)
        self.on_create(file_name)
        
        self.SetDropTarget(parent.FileDrop(parent))
        self.Bind(wx.EVT_CLOSE, self.on_close)

    def on_close(self, eve):
        self.Destroy()

    def on_create(self, path):
        if path:
            with open(path,"r",encoding="utf-8") as f:
                self.ChangeValue(f.read())

    def OnUpdateUI(self, evt):
        braceAtCaret = -1
        braceOpposite = -1
        charBefore = None
        caretPos = self.GetCurrentPos()
        if caretPos > 0:
            charBefore = self.GetCharAt(caretPos - 1)
            styleBefore = self.GetStyleAt(caretPos - 1)
        if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
            braceAtCaret = caretPos - 1
        if braceAtCaret < 0:
            charAfter = self.GetCharAt(caretPos)
            styleAfter = self.GetStyleAt(caretPos)
            if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
                braceAtCaret = caretPos
        if braceAtCaret >= 0:
            braceOpposite = self.BraceMatch(braceAtCaret)
        if braceAtCaret != -1  and braceOpposite == -1:
            self.BraceBadLight(braceAtCaret)
        else:
            self.BraceHighlight(braceAtCaret, braceOpposite)

    def OnMarginClick(self, evt):
        if evt.GetMargin() == 2:
            if evt.GetShift() and evt.GetControl():
                lineCount = self.GetLineCount()
                expanding = True
                for lineNum in range(lineCount):
                    if self.GetFoldLevel(lineNum) & stc.STC_FOLDLEVELHEADERFLAG:
                        expanding = not self.GetFoldExpanded(lineNum)
                        break
                lineNum = 0
                while lineNum < lineCount:
                    level = self.GetFoldLevel(lineNum)
                    if level & stc.STC_FOLDLEVELHEADERFLAG and (level & stc.STC_FOLDLEVELNUMBERMASK) == stc.STC_FOLDLEVELBASE:
                        if expanding:
                            self.SetFoldExpanded(lineNum, True)
                            lineNum = self.Expand(lineNum, True)
                            lineNum = lineNum - 1
                        else:
                            lastChild = self.GetLastChild(lineNum, -1)
                            self.SetFoldExpanded(lineNum, False)
                            if lastChild > lineNum:
                                self.HideLines(lineNum+1, lastChild)
                    lineNum = lineNum + 1
            else:
                lineClicked = self.LineFromPosition(evt.GetPosition())
                if self.GetFoldLevel(lineClicked) & stc.STC_FOLDLEVELHEADERFLAG:
                    if evt.GetShift():
                        self.SetFoldExpanded(lineClicked, True)
                        self.Expand(lineClicked, True, True, 1)
                    elif evt.GetControl():
                        if self.GetFoldExpanded(lineClicked):
                            self.SetFoldExpanded(lineClicked, False)
                            self.Expand(lineClicked, False, True, 0)
                        else:
                            self.SetFoldExpanded(lineClicked, True)
                            self.Expand(lineClicked, True, True, 100)
                    else:
                        self.ToggleFold(lineClicked)

    def Expand(self, line, doExpand, force=False, visLevels=0, level=-1):
        lastChild = self.GetLastChild(line, level)
        line = line + 1
        while line <= lastChild:
            if force:
                if visLevels > 0:
                    self.ShowLines(line, line)
                else:
                    self.HideLines(line, line)
            else:
                if doExpand:
                    self.ShowLines(line, line)
            if level == -1:
                level = self.GetFoldLevel(line)
            if level & stc.STC_FOLDLEVELHEADERFLAG:
                if force:
                    if visLevels > 1:
                        self.SetFoldExpanded(line, True)
                    else:
                        self.SetFoldExpanded(line, False)
                    line = self.Expand(line, doExpand, force, visLevels-1)
                else:
                    if doExpand and self.GetFoldExpanded(line):
                        line = self.Expand(line, True, force, visLevels-1)
                    else:
                        line = self.Expand(line, False, force, visLevels-1)
            else:
                line = line + 1
        return line




def py_key(parent):
    keywords_key = ["abs","all","any","ascii","async","await","bin","bool","bytearray","bytes","callable","chr","classmethod",
        "compile","complex","delattr","dict","dir","divmod","enumerate","eval","exec","filter","float","format","frozenset",
        "getattr","globals","hasattr","hash","help","hex","id","input","int","isinstance","issubclass","iter","len","list",
        "locals","map","max","memoryview","min","next","object","oct","open","ord","pow","print","property","range","repr",
        "reversed","round","set","setattr","slice","sorted","staticmethod","str","sum","super","tuple","type","vars","zip",
        "False","None","True","and","as","assert","break","class","continue","def","del","elif","else","except","finally",
        "for","from","global","if","import","in","is","lambda","nonlocal","not","or","pass","raise","return","try","while",
        "with","yield"
        ]
    parent.SetKeyWords(0, " ".join(keywords_key))


def lua_key(parent):
    keywords_key = ["elseif","end","false","for","function","if","in","local","nil","not","or","repeat","return","then",
        "true","until","while","goto","print","else","and","do","break","_G","_VERSION","assert","collectgarbage",
        "dofile","error","getmetatable","ipairs","load","loadfile","next","pairs","pcall","rawget","rawequal","rawlen",
        "rawset","require","select","setmetatable","tonumber","tostring","type","warn","xpcall","__add","__band","__bnot",
        "__bor","__bxor","call","__close","__concat","__div","__eq","__gc","__idiv","__index","__le","__len","__lt","__metatable",
        "__mod","__mode","__mul","__name","__newindex","__pairs","__pow","__shl","__shr","__sub","__tostring","__unm"                    
    ]
    parent.SetKeyWords(0, " ".join(keywords_key))


def go_key(parent):
    keywords_key = ["break","case","chan","const","continue","default","defer","else","fallthrough","for","func","go","goto",
        "if","import","interface","map","package","return","range","select","struct","switch","type","var","len","make","close",
        "new","cap","append","string","delete","panic","recover","real","imag","copy","bool","error","int","int8",
        "int16","int32","int64","uint","uint8","uint16","uint32","uint64","float32","float64","complex64","complex128",
        "byte","false","true","_"
    ]
    parent.SetKeyWords(0, " ".join(keywords_key))


def rust_key(parent):
    keywords_key = ["as","async","await","break","const","continue","crate","dyn","else","enum","extern","false","fn","for","if",
    "impl","in","let","loop","match","mod","move","mut","pub","ref","return","Self","self","static","struct","super","trait","true",
    "type","union","unsafe","use","where","while"
    ]
    parent.SetKeyWords(0, " ".join(keywords_key))




class FileDrop(wx.FileDropTarget):
    def __init__(self,windows):
        wx.FileDropTarget.__init__(self)

def main():
    app = wx.App()
    ex = wx.Frame(None)
    ex.FileDrop = FileDrop
    Book_stc(ex, None, stc.STC_LEX_PYTHON)
    ex.Show()
    app.MainLoop()


if __name__ == '__main__':
    main()